package cn.xuguowen.user.websocket;

import cn.xuguowen.config.GarbageWebSocketServerConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * ClassName: XuGuoWenWebSocket
 * Package: cn.xuguowen.user.websocket
 * Description:
 *
 * @Author 徐国文
 * @Create 2023/5/22 0:18
 * @Version 1.0
 */
@Component
@ServerEndpoint(value = "/xuguowen",configurator = GarbageWebSocketServerConfig.class)
@Slf4j
public class XuGuoWenWebSocket {
    /**
     * 记录当前的连接数
     * 注意线程的安全问题。同一时刻如果有多个客户端连接的时候，记录的连接数要是正确的！
     */
    private static AtomicInteger onLineCount = new AtomicInteger(0);

    /**
     * 存放所有在线的客户端
     * 注意线程的安全问题。同一时刻如果有多个客户端的时候，记录的客户端要是正确的！
     */
    private static Map<String,XuGuoWenWebSocket> clients = new ConcurrentHashMap<>();

    /**
     * 当前websocket对象与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    private Session session;

    /**
     * erp唯一标识
     */
    private String erp = "";

    /**
     * 当客户端连接到我的websocket服务器端的时候要执行onOpen方法
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        try {
            // 刚才在GarbageWebSocketServerConfig类中的modifyHandshake方法中有见到getUserProperties方法的影子
            Map<String, Object> userProperties = config.getUserProperties();
            String erp = (String) userProperties.get("erp");
            this.erp = erp;
            this.session = session;

            // 如果你已经在其他端进行连接了，然后你又要在此端进行连接，是要将其他端进行断开的
            if (clients.containsKey(this.erp)) {
                // 关闭连接
                clients.get(this.erp).session.close();
                // 从clients集合中移除
                clients.remove(this.erp);
                // 在线连接数-1
                onLineCount.decrementAndGet();
            }


            // 正常连接的情况下，要将其加入到集合中，在线连接数+1，服务器端给客户端发送消息
            clients.put(this.erp,this);
            onLineCount.incrementAndGet();
            log.info("有新连接加入：{}，当前在线人数为：{}", erp, onLineCount.get());
            sendMessage("连接成功",this.session);
        } catch (IOException e) {
            log.error("建立链接错误{}", e.getMessage(), e);
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        try {
            if (clients.containsKey(this.erp)) {
                clients.get(this.erp).session.close();
                clients.remove(this.erp);
                onLineCount.decrementAndGet();
            }
            log.info("有一连接关闭：{}，当前在线人数为：{}", this.erp, onLineCount.get());
        } catch (Exception e) {
            log.error("连接关闭错误，错误原因{}", e.getMessage(), e);
        }
    }

    /**
     * 连接失败可以调用这个方法
     * @param session       当前连接会话
     * @param error         错误信息
     */
    @OnError
    public void onError(Session session, Throwable error) {
        // 注意打印异常的代码！！！
        // 为什么是两个{}，而出现了3个参数
        log.error("Socket:{},发生错误,错误原因{}", erp, error.getMessage(), error);
        try {
            session.close();
        } catch (Exception e) {
            log.error("onError.Exception{}", e.getMessage(), e);
        }
    }

    /**
     * 客户端给服务器端发送消息时，会调用如下的方法
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("服务端收到客户端[{}]的消息:{}", this.erp, message);
        // 心跳机制。因为websocket长时间没有反应，会出现断开连接的！
        if (message.equals("ping")) {
            this.sendMessage("pong", session);
        }
    }

    /**
     * 服务器端给客户端发送消息，指定发送消息
     * @param message       消息本身
     * @param session       当前客户端会话
     */
    private void sendMessage(String message, Session session) {
        log.info("服务端给客户端[{}]发送消息:{}", this.erp, message);
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.error("{}发送消息发生异常，异常原因{}", this.erp, message);
        }
    }

    /**
     * 群发消息
     */
    public void sendMessage(String message) {
        for (Map.Entry<String, XuGuoWenWebSocket> sessionEntry : clients.entrySet()) {
            String erp = sessionEntry.getKey();
            XuGuoWenWebSocket socket = sessionEntry.getValue();
            Session session = socket.session;
            log.info("服务端给客户端[{}]发送消息{}", erp, message);
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("{}发送消息发生异常，异常原因{}", this.erp, e.getMessage(),e);
            }
        }
    }
}
